package com.ymx.darling.log.jdbc;

import com.ymx.darling.log.Log;
import com.ymx.darling.util.ExceptionUtil;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;

/**
 * @author 爱Java的小于
 */
public class PreparedStatementLogger extends BaseJdbcLogger implements InvocationHandler {
    private final PreparedStatement preparedStatement;

    public PreparedStatementLogger(PreparedStatement preparedStatement, Log statementLogger) {
        super(statementLogger);
        this.preparedStatement = preparedStatement;
    }


    public static PreparedStatement newInstance(PreparedStatement stmt, Log statementLogger) {
        InvocationHandler handler = new PreparedStatementLogger(stmt, statementLogger);
        ClassLoader cl = PreparedStatement.class.getClassLoader();
        return (PreparedStatement) Proxy.newProxyInstance(cl, new Class[]{PreparedStatement.class, CallableStatement.class}, handler);
    }


    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        try {
            if (Object.class.equals(method.getDeclaringClass())) {
                return method.invoke(this, args);
            } else {
                ResultSet rs;
                if (EXECUTE_METHODS.contains(method.getName())) {
                    if (this.isDebugEnabled()) {
                        this.debug(" Parameters: " + this.getParameterValueString(), true);
                    }

                    this.clearColumnInfo();
                    if ("executeQuery".equals(method.getName())) {
                        rs = (ResultSet) method.invoke(this.preparedStatement, args);

                        return rs == null ? null : ResultSetLogger.newInstance(rs, this.getStatementLogger());
                    } else if ("executeUpdate".equals(method.getName())) {
                        int update = (int)method.invoke(this.preparedStatement, args);
                        this.debug("    Updates: " + update, false);

                        return update;
                    } else {
                        return method.invoke(this.preparedStatement, args);
                    }

                } else if (SET_METHODS.contains(method.getName())) {
                    if ("setNull".equals(method.getName())) {
                        this.setColumn(args[0], null);
                    } else {
                        this.setColumn(args[0], args[1]);
                    }

                    return method.invoke(this.preparedStatement, args);
                } else if ("getResultSet".equals(method.getName())) {
                    rs = (ResultSet)method.invoke(this.preparedStatement, args);

                    return rs == null ? null : ResultSetLogger.newInstance(rs, this.getStatementLogger());
                } else if ("getUpdateCount".equals(method.getName())) {
                    int updateCount = (Integer)method.invoke(this.preparedStatement, args);
                    if (updateCount != -1) {
                        this.debug("    Updates: " + updateCount, false);
                    }

                    return updateCount;
                } else {

                    return method.invoke(this.preparedStatement, args);
                }
            }
        } catch (Throwable throwable) {
            throw ExceptionUtil.unwrapThrowable(throwable);
        }
    }
}
